/**
 * Copyright (c) 2007-2008 Nearchos Paspallis
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * AspectSense
 *
 * Middleware for annotation-based, context-aware and adaptive services for
 * mobile and ubiquitous computing
 *
 * This project is an open-source effort, initiated by Nearchos Paspallis as
 * part of his PhD work at the University of Cyprus (http://www.ucy.ac.cy).
 *
 * More information about the project is available at "http://aspectsense.org".
 */

package org.aspectsense.framework;

import static org.aspectsense.framework.ComponentLifecycleState.SUSPENDED;
import org.aspectsense.metadata.ComponentMetadata;
import org.aspectsense.metadata.MetadataFactory;

import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Logger;

/**
 * There is exactly one {@link ComponentContainerPool} per
 * {@link ComponentContainer} (and consequently per {@link Component}). A pool
 * contains multiple instances of the same {@link Component} type and is used
 * for caching purposes.
 *
 * <p/>
 * Date: Jul 13, 2007<br/>
 * Time: 10:02:11 AM
 * </p>
 *
 * @author Nearchos Paspallis [nearchos@cs.ucy.ac.cy]
 */
class ComponentContainerPool
{
    private static final Logger logger = Logger.getLogger(ComponentContainerPool.class.getCanonicalName());

    private final Class componentType;

    private final Class providedServiceType;

    private final ComponentMetadata componentMetadata;

    private final Queue<ComponentContainer> unusedComponentContainers;

    ComponentContainerPool(final Class componentType, final Class providedServiceType)
    {
        if(componentType == null)
        {
            throw new NullPointerException("Illegal null argument");
        }

        this.componentType = componentType;
        this.componentMetadata = MetadataFactory.getComponentMetadata(componentType);
        this.providedServiceType = providedServiceType;
        this.unusedComponentContainers = new LinkedBlockingQueue<ComponentContainer>();
    }

    Class getComponentType()
    {
        return componentType;
    }

    Class getProvidedServiceType()
    {
        return providedServiceType;
    }

    public ComponentMetadata getComponentMetadata()
    {
        return componentMetadata;
    }

    // -------------- Access interface to Component containers -------------- //

    /**
     * This method is used to get an instance of a {@link Component} from
     * the pool. {@link Stateful} instances are removed from the queue, while
     * for stateless components only a reference is copied.
     *
     * @return an instance of {@link ComponentContainer} encapsulating the
     *         requested {@link Component}
     */
    synchronized ComponentContainer takeComponentContainer()
    {
        // lazy instantiation
        if(unusedComponentContainers.isEmpty())
        {
            logger.finest("Creating a new instance of " + componentMetadata.getComponentTypeCanonicalName());
            try
            {
                unusedComponentContainers.add(new ComponentContainer(this));
            }
            catch (ConfigurationException ce)
            {
                throw new RuntimeException("Error while instantiating component: " + componentType, ce);
            }
        }

        if(componentMetadata.isStateful())
        {
            // For stateful components, remove and return the requested
            // component instance ...
            logger.finest("Removing and returning a stateful instance of "
                    + componentMetadata.getComponentTypeCanonicalName());
            return unusedComponentContainers.poll();
        }
        else
        {
            // ... while for non-stateful components, always return the same
            // copy of the component instance
            logger.finest("Returning a reference to the stateless instance of "
                    + componentMetadata.getComponentTypeCanonicalName());
            return unusedComponentContainers.peek();
        }
    }

    /**
     * This method is used to return a component to the pool after it has been
     * used. {@link Stateful} instances must be returned for efficiency reasons,
     * while stateless ones are returned for consistency only (they are actually
     * ignored by the implementation of this method).
     *
     * @param componentContainer the {@link ComponentContainer} to be returned
     *     to the pool
     */
    synchronized void returnComponentContainer(final ComponentContainer componentContainer)
    {
        if(componentMetadata.isStateful())
        {
            if(componentContainer.getComponentLifecycleState() == SUSPENDED)
            {
                unusedComponentContainers.add(componentContainer);
            }
            else
            {
                logger.warning("Attempted to return a component while in state: "
                        + componentContainer.getComponentLifecycleState() + ". Reuse is skipped!");
            }
        }
        // else nothing, as stateless components are never really removed from
        // the pool
    }

    // ----------- End of access interface to Component containers ---------- //
}